﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace XcoAppSpaces.Transport.Sockets.Auth
{
	public class ExpiringEntry<T>
	{
		public DateTime LastAdded { get; set; }
		public int NumberOfAdds { get; set; }
		public T Item { get; set; }
	}

	public class ExpiringEntriesList<T>
	{
		private readonly List<ExpiringEntry<T>> entries = new List<ExpiringEntry<T>>();
		private readonly int entrylifeTimeSeconds;

		private Timer timer;

		public ExpiringEntriesList(int entrylifeTimeSeconds, int checkInterval)
		{
			this.entrylifeTimeSeconds = entrylifeTimeSeconds;
			this.timer = new Timer(_ => RemoveOldEntries(), null, checkInterval, checkInterval);
		}


		public ExpiringEntry<T> GetOrAdd(T item)
		{
			lock (entries)
			{
				var entry = entries.FirstOrDefault(e => e.Item.Equals(item));
				if (entry == null)
				{
				    entry = CreateInstance();
				    entry.Item = item;
				    entry.NumberOfAdds = 0;
					entries.Add(entry);
				}
				entry.LastAdded = DateTime.Now;
				entry.NumberOfAdds++;
				return entry;
			}
		}

		public void Remove(ExpiringEntry<T> entry)
		{
			lock (entries)
			{
				entries.Remove(entry);
			}
		}

		public void Remove(T item)
		{
			lock (entries)
			{
				var entry = entries.FirstOrDefault(e => e.Item.Equals(item));
				if (entry != null)
					entries.Remove(entry);
			}
		}

		public bool Contains(T item)
		{
			lock (entries)
			{
				return entries.Any(e =>
				{
					var result = e.Item.Equals(item);
					return result;
				});
			}
		}

		private void RemoveOldEntries()
		{
			var checkDate = DateTime.Now.AddSeconds(-entrylifeTimeSeconds);
			lock (entries)
			{
				for (int i = 0; i < entries.Count; i++)
				{
					if (entries[i].LastAdded < checkDate)
					{
						entries.RemoveAt(i);
						i--;
					}
				}
			}
		}

		public void Dispose()
		{
			if (timer != null)
				timer.Dispose();
		}

        protected virtual ExpiringEntry<T> CreateInstance()
        {
            return new ExpiringEntry<T>();
        }
	}
}
